package com.salt.core.redis.config;

import com.salt.common.utils.StringUtils;
import com.salt.core.redis.cache.SaltRedis;
import com.salt.core.redis.properties.SaltLettuceProperties;
import com.salt.core.redis.properties.SaltRedisProperties;
import com.salt.core.redis.properties.SaltRedisSerializerProperties;
import com.salt.core.redis.serializer.ProtoStuffSerializer;
import com.salt.core.redis.serializer.RedisKeySerializer;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.time.Duration;

/**
 * @Description
 * @Author salt
 * @Date 2021/12/14
 * @Version 1.0.1
 */
@Configuration
@EnableCaching
@EnableConfigurationProperties({SaltRedisSerializerProperties.class, SaltRedisProperties.class})
public class RedisAutoConfiguration {

    /*@Value("${spring.redis.database}")
    private int database;
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private long timeout;  // 连接超时时间
    //在关闭客户端连接之前等待任务处理完成的最长时间，在这之后，无论任务是否执行完成，都会被执行器关闭，默认100ms
    @Value("${spring.redis.lettuce.shutdown-timeout}")
    private long shutDownTimeout;
    @Value("${spring.redis.lettuce.pool.max-idle}")
    private int maxIdle;
    @Value("${spring.redis.lettuce.pool.min-idle}")
    private int minIdle;
    @Value("${spring.redis.lettuce.pool.max-active}")
    private int maxActive;
    @Value("${spring.redis.lettuce.pool.max-wait}")
    private long maxWait;*/

    @Bean
    @ConditionalOnMissingBean(RedisSerializer.class)
    public RedisSerializer<Object> redisSerializer(SaltRedisSerializerProperties saltRedisSerializerProperties){
        SaltRedisSerializerProperties.SerializerType serializerType = saltRedisSerializerProperties.getSerializerType();
        if (SaltRedisSerializerProperties.SerializerType.JDK == serializerType) {
            ClassLoader loader = this.getClass().getClassLoader();
            return new JdkSerializationRedisSerializer(loader);
        }
        if (SaltRedisSerializerProperties.SerializerType.ProtoStuff == serializerType) {
            return new ProtoStuffSerializer();
        }
        return new GenericJackson2JsonRedisSerializer();
    }

    @Primary
    @Bean("lettuceConnectionFactory")
    public LettuceConnectionFactory lettuceConnectionFactory(SaltRedisProperties saltRedisProperties) {
        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        SaltLettuceProperties lettuce = saltRedisProperties.getSaltLettuceProperties();
        SaltLettuceProperties.Pool lettucePool = lettuce.getPool();

        genericObjectPoolConfig.setMaxIdle(lettucePool.getMaxIdle());
        genericObjectPoolConfig.setMinIdle(lettucePool.getMinIdle());
        genericObjectPoolConfig.setMaxTotal(lettucePool.getMaxActive());
        genericObjectPoolConfig.setMaxWaitMillis(lettucePool.getMaxWait());
        genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(100);

        // 单机版配置
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setDatabase(saltRedisProperties.getDatabase());
        redisStandaloneConfiguration.setHostName(saltRedisProperties.getHost());
        redisStandaloneConfiguration.setPort(saltRedisProperties.getPort());
        if (StringUtils.isNotBlank(saltRedisProperties.getPassword()))
            redisStandaloneConfiguration.setPassword(RedisPassword.of(saltRedisProperties.getPassword()));

        LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
                .commandTimeout(Duration.ofMillis(saltRedisProperties.getTimeout()))
                .shutdownTimeout(Duration.ofMillis(lettuce.getShutDownTimeout()))
                .poolConfig(genericObjectPoolConfig)
                .build();

        LettuceConnectionFactory factory = new LettuceConnectionFactory(redisStandaloneConfiguration, clientConfig);
        return factory;
    }

    @Bean
    @Primary
    public CacheManager cacheManager(
            @Qualifier("lettuceConnectionFactory") LettuceConnectionFactory lettuceConnectionFactory, RedisSerializer<Object> redisSerializer) {

        //初始化一个RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(lettuceConnectionFactory);
        //设置CacheManager的值序列化方式为JdkSerializationRedisSerializer,但其实RedisCacheConfiguration默认就是使用StringRedisSerializer序列化key，JdkSerializationRedisSerializer序列化value,所以以下注释代码为默认实现
        //ClassLoader loader = this.getClass().getClassLoader();
        //JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(loader);
        RedisSerializationContext.SerializationPair<Object> valuePair =
                RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer);

        RedisCacheConfiguration defaultCacheConfig =
                RedisCacheConfiguration.defaultCacheConfig()
                        .serializeValuesWith(valuePair);
        //初始化RedisCacheManager
        RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
        return cacheManager;
    }


    @Bean
    public RedisTemplate<String,Object> redisTemplate(
            @Qualifier("lettuceConnectionFactory") LettuceConnectionFactory lettuceConnectionFactory, RedisSerializer<Object> redisSerializer) {

        /*RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // key 序列化
        RedisKeySerializer redisKeySerializer = new RedisKeySerializer();
        redisTemplate.setKeySerializer(redisKeySerializer);
        redisTemplate.setHashKeySerializer(redisKeySerializer);
        // value 序列化
        //redisTemplate.setValueSerializer(redisSerializer);
        //redisTemplate.setHashValueSerializer(redisSerializer);
        GenericJackson2JsonRedisSerializer jsonRedisSerializer =
                new GenericJackson2JsonRedisSerializer();
        redisTemplate.setValueSerializer(jsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jsonRedisSerializer);

        redisTemplate.setConnectionFactory(connectionFactory);*/
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // key 序列化
        RedisKeySerializer redisKeySerializer = new RedisKeySerializer();
        //redisTemplate.setKeySerializer(redisKeySerializer);
        //redisTemplate.setKeySerializer(keySerializer());
        redisTemplate.setHashKeySerializer(redisKeySerializer);
        // value 序列化
        redisTemplate.setValueSerializer(redisSerializer);
        redisTemplate.setHashValueSerializer(redisSerializer);

        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        return redisTemplate;
    }

    @Bean
    @ConditionalOnMissingBean(ValueOperations.class)
    public ValueOperations valueOperations(RedisTemplate<String,Object> redisTemplate){
        return redisTemplate.opsForValue();
    }

    @Bean
    public SaltRedis saltRedis(RedisTemplate<String,Object> redisTemplate){
        return new SaltRedis(redisTemplate);
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(@Qualifier("lettuceConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

}
